﻿namespace Sai.Core
{
	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.IO;
	using System.Xml.Serialization;
	using Sai.Core.Reporting;
    using System.Xml;
    using System.Text.RegularExpressions;
	
	/// <summary>
	/// Project object used to store options for configuration of one product.
	/// Can be persisted to file or created on the fly and discarded.
	/// </summary>
	[XmlRoot("SaiProject")]
    public class ProductReleaseProject : IProductReleaseProject
	{
        // report on protected next !
		private Identifier identifier = new Identifier();
        private HashSet<string> fromFiles = new HashSet<string>();
        private HashSet<string> toFiles = new HashSet<string>();

		private readonly ReportFormat DefaultReportFormat = ReportFormat.Html;
		private readonly string DefaultReportStyle = "Blue";

		public ProductReleaseProject()
		{
			this.ReportFormat = DefaultReportFormat;
			this.ReportStyleName = DefaultReportStyle;
		}

        public ProductReleaseProject(string v)
            : this()
        {
            this.Version = v;
        }

		public ProductReleaseProject(Version v)
			: this()
		{
			this.Version = v.ToString(2);
		}

		/// <summary>
		/// The name of the file where the project was read from. May be empty
		/// if project created on the fly.
		/// </summary>
		[XmlIgnore]
		public string FileName
		{
            get;
            set;
        }

		/// <summary>
		/// The version of the project on file.
		/// </summary>
		[XmlAttribute("Version")]
		public string Version
		{
			get;
			set;
		}

		/// <summary>
		/// A unqiue ID for this project.
		/// </summary>
		[XmlAttribute("ID")]
		public string ID
		{
			get
			{
				return this.identifier.Value;
			}

			set
			{
				this.identifier = new Identifier(value);
			}
		}

		/// <summary>
		/// The product name.
		/// </summary>
		[XmlAttribute("Name")]
		public string Name
		{
			get;
			set;
		}

		// project options 

		// release needs to be simply version and list of file names...

		/// <summary>
		/// The name of the previous version.
		/// </summary>
		public string FromVersion
		{
			get;
			set;
		}

		/// <summary>
		/// The folder containing the previous version of the API.
		/// </summary>
		public string FromFolder
		{
			get;
			set;
		}

		/// <summary>
		/// Gets a collection of assemblies comprising the previous version of the API.
		/// </summary>
        [XmlArray]
        [XmlArrayItem(typeof(string), ElementName = "Assembly")]
        public HashSet<string> FromAssemblies
		{
			get
			{
				return this.fromFiles;
			}
		}

		/// <summary>
		/// The folder containing the new version of the API.
		/// </summary>
		public string ToFolder
		{
			get;
			set;
		}

		/// <summary>
		/// A collection of assemblies comprising the new version of the API.
		/// </summary>
        [XmlArray]
        [XmlArrayItem(typeof(string), ElementName="Assembly")]
        public HashSet<string> ToAssemblies
		{
			get
			{
				return this.toFiles;
			}
		}

		/// <summary>
		/// The name of the new version.
		/// </summary>
		public string ToVersion
		{
			get;
			set;
		}
		
		/// <summary>
		/// Where to place the output reports.
		/// </summary>
		public string OutputFolder
		{
			get;
			set;
		}

		/// <summary>
		/// How reports are created.
		/// </summary>
		public ReportFormat ReportFormat
		{
			get;
			set;
		}

		public string ReportStyleName
		{
			get;
			set;
		}

        private IReportStyler _reportStyler = null;

		/// <summary>
		/// Custom content provider.
		/// </summary>
        [XmlIgnore]
        public IReportStyler ReportStyler
		{
            get
            {
                return this._reportStyler;
            }
            set
            {
                this._reportStyler = value;

                if (this._reportStyler == null)
                    return;

                // override report style if we have something in the 
                // project file...
                if (!string.IsNullOrEmpty(this.HeadTagText))
                {
                    this._reportStyler.HeadTag = this.HeadTagText;
                }

                if (!string.IsNullOrEmpty(this.StyleTagText))
                {
                    this._reportStyler.StyleTag = this.StyleTagText;
                }

                if (!string.IsNullOrEmpty(this.HeadingTextInternal))
                {
                    this._reportStyler.HeadingText = this.HeadingTextInternal;
                }

                if (!string.IsNullOrEmpty(this.FooterTextInternal))
                {
                    this._reportStyler.FooterText = this.FooterTextInternal;
                }
            }
        }

		[XmlIgnore]
		public IReportTransformation SummaryTransformer
		{
			get;
			set;
		}

		[XmlIgnore]
		public IReportTransformation AssemblyTransformer
		{
			get;
			set;
		}

		[XmlIgnore]
		public IReportTransformation TypeTransformer
		{
			get;
			set;
		}

        [XmlIgnore]
        public IReportTransformation ListingTransformer
        {
            get;
            set;
        }

		public bool CleanOutput
		{
			get;
			set;
		}

		/// <summary>
		/// Optional sandcastle project file (SHFB) when outputting MAML content reports.
		/// </summary>
		public string SandcastleProject
		{
			get;
			set;
		}

		/// <summary>
		/// Optional topic id to replace within content layout file in sandcastle.
		/// </summary>
		public string TopicId
		{
			get;
			set;
		}

        public bool ReportOnRemovals { get; set; }

        public bool ReportOnAdditions { get; set; }

        public bool ReportOnChanges { get; set; }

        public bool ReportOnObsolete { get; set; }

        public XmlCDataSection HeadTag
        {
            get
            {
                XmlDocument doc = new XmlDocument();
                return doc.CreateCDataSection(this.HeadTagText);
            }
            set
            {
                this.HeadTagText = value.Value;
            }
        }
        
        [XmlIgnore]
        public string HeadTagText { get; set;  }

        public XmlCDataSection StyleTag
        {
            get
            {
                XmlDocument doc = new XmlDocument();
                return doc.CreateCDataSection(this.StyleTagText);
            }
            set
            {
                this.StyleTagText = value.Value;
            }
        }

        [XmlIgnore]
        public string StyleTagText { get; set; }

        public XmlCDataSection HeadingText
        {
            get
            {
                XmlDocument doc = new XmlDocument();
                return doc.CreateCDataSection(this.HeadingTextInternal);
            }
            set
            {
                this.HeadingTextInternal = value.Value;
            }
        }

        [XmlIgnore]
        public string HeadingTextInternal { get; set; }

        public XmlCDataSection FooterText
        {
            get
            {
                XmlDocument doc = new XmlDocument();
                return doc.CreateCDataSection(this.FooterTextInternal);
            }
            set
            {
                this.FooterTextInternal = value.Value;
            }
        }

        [XmlIgnore]
        public string FooterTextInternal { get; set; }

		public void AddFromFile(string fileName)
		{
			this.fromFiles.Add(fileName);
		}

		public void AddFromFiles(IEnumerable<string> fileNames)
		{
            foreach (string fileName in fileNames)
            {
                // check for relativity

                this.fromFiles.Add(fileName);
            }
		}

		public void AddToFile(string fileName)
		{
			this.toFiles.Add(fileName);
		}

		public void AddToFiles(IEnumerable<string> fileNames)
		{
            foreach (string fileName in fileNames)
            {
                this.toFiles.Add(fileName);
            }
		}
		
		public void Save()
		{
			this.SaveTo(this.FileName);
		}

		public void SaveTo(string toFile)
		{
            // play trick with file paths to make them relative if possible.

			using (var writer = new StreamWriter(toFile))
			{
				this.SaveTo(writer);
			}

            // restore...
		}

		public void SaveTo(TextWriter writer)
		{
            ObjectFlattener<ProductReleaseProject>.Flatten(this, writer);
		}
	}
}
